#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sqlite3.h>
#include <signal.h>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <wait.h>

#define N 16 //用户名和字段名最大长度
#define R 1 //用户注册操作码
#define L 2 //用户登录操作码
#define Q 3 //查询单词操作码
#define H 4 //查询历史记录操作码

#define DATABASE "dictionary.db" //数据库文件名

typedef struct{
    int type; //操作码 R/L/Q/H
    char name[N]; //用户名
    char data[256]; //密码/单词/查询结果
}MSG;

//僵尸进程处理函数
void handler(int sig){
    wait(NULL); //回收子进程
}

//注册处理函数
void handle_register(int connectfd, MSG *msg, sqlite3 *db){
    char sqlstr[512] = {0};
    char *errmsg;
    sprintf(sqlstr, "insert into user values('%s', '%s');", msg->name, msg->data);

    if(sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK){
        sprintf(msg->data, "用户 %s 已经存在了", msg->name);
        
    }else{
        strcpy(msg->data, "注册成功");
    }
    send(connectfd, msg, sizeof(MSG), 0);
    
}

//登录处理函数
void handle_login(int connectfd, MSG *msg, sqlite3 *db){
    char sqlstr[512] = {0};
    char *errmsg;
    char **result;
    int nrow,ncolumn;
    sprintf(sqlstr, "select * from user where name='%s' and pass='%s';", msg->name, msg->data);

    if(sqlite3_get_table(db, sqlstr, &result, &nrow, &ncolumn, &errmsg) != SQLITE_OK){
        printf("查询失败:%s\n", errmsg);
    }
    if(nrow == 0){
        //无匹配记录
        strcpy(msg->data, "用户名或密码错误!!!");
    }else{
        //有匹配记录
        strncpy(msg->data, "OK", 256);
    }
    send(connectfd, msg, sizeof(MSG), 0);
}

int handle_searchword(int connectfd,MSG *msg){
    FILE *fp;
    //存储词典文件每行内容
    char temp[300];
    char *p;

    //查询单词长度
    int len = strlen(msg->data);

    if((fp = fopen("dict.txt", "r")) == NULL){
        strcpy(msg->data, "词典文件打开失败");
        send(connectfd, msg, sizeof(MSG), 0);
        return 0;
    }
    while(fgets(temp, 300, fp) != NULL){
        int result = strncmp(msg->data, temp, len);

        //精确匹配：匹配单词且后面是空格
        if(result == 0 && temp[len] == ' '){
            p = temp + len; //移动到单词末尾
            while(*p== ' ') p++; //跳过空格

            //复制解释
            strcpy(msg->data, p);
            fclose(fp);
            return 1;
        }
    }

    strcpy(msg->data, "未找到该单词");
    fclose(fp);
    return 0;
}

void getdata(char *data){
    time_t t;
    struct tm *tp;
    time(&t); //获取当前时间
    tp = localtime(&t); //转换为本地时间

    //格式化时间字符串YYY-MM-DD HH:MM:SS
    sprintf(data, "%d-%02d-%02d %02d:%02d:%02d", tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec);
}

//执行查询函数
void handle_query(int connectfd, MSG *msg, sqlite3 *db){
    char sqlstr[512],*errmsg;
    char date[128],word[128];
    int found = 0;

    //保存原始单词
    strcpy(word,msg->data);

    //进行查询处理(单词查询)
    found = handle_searchword(connectfd, msg);

    if(found == 1){
        //查找到单词
        //保存历史
        //获取时间
        getdata(date);
        sprintf(sqlstr, "insert into record values('%s', '%s', '%s');", msg->name, date, word);

        //执行sql语句
        if(sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg) != SQLITE_OK){
            printf("插入记录失败:%s\n", errmsg);
        }
    }
    //返回查询结果
    send(connectfd, msg, sizeof(MSG), 0);
}

//历史记录回调函数
int history_callback(void *arg, int f_num, char **f_value, char **f_name){
    int connectfd = *(int*)arg; //获取连接套接字
    MSG msg;
    
    //时间：单词
    sprintf(msg.data, "%s %s", f_value[1], f_value[2]);
    send(connectfd, &msg, sizeof(MSG), 0);//发送给客户端
    return 0;//继续处理下一条语句
}

//执行查询历史记录函数
void handle_history(int connectfd, MSG *msg, sqlite3 *db){
    char sqlstr[512],*errmsg;

    //sql语句初始化
    sprintf(sqlstr, "select * from record where name='%s';", msg->name);

    if(sqlite3_exec(db, sqlstr, history_callback, (void*)&connectfd, &errmsg) != SQLITE_OK){
        printf("查询历史记录失败:%s\n", errmsg);
        sqlite3_free(errmsg);
    }

    strcpy(msg->data, "**OVER**");//结束标志
    send(connectfd, msg, sizeof(MSG), 0);//发送给客户端
}

//处理客户端子进程函数
void handle_client(int connectfd, sqlite3 *db){
    MSG msg;
    while(recv(connectfd, &msg, sizeof(msg), 0) > 0){
        printf("操作类型：%d\n",msg.type);
        printf("数据data：%s\n",msg.data);
        switch (msg.type){
            case R://注册
                //调用注册处理函数
                handle_register(connectfd, &msg, db);
                break;
            case L://登录
                //调用登录处理函数
                handle_login(connectfd, &msg, db);
                break;
            case Q://查询单词
                //调用查询单词处理函数
                handle_query(connectfd, &msg, db);
                break;
            case H://查询历史记录
                //调用查询历史记录处理函数
                handle_history(connectfd, &msg, db);
                break;
            default:
                break;
        }
    }
    printf("客户端退出！\n");
    close(connectfd);
    return;
}

int main(int argc, char *argv[]){

    //声明
    int listenfd, connectfd;//监听套接字和连接套接字
    struct sockaddr_in server_addr; //服务器地址结构
    pid_t pid; //进程ID用于多进程
    sqlite3 *db; //sqlite3数据库指针

    if(argc < 3){
        printf("格式：%s <IP> <端口>\n", argv[0]);
        return 1;
    }

    if(sqlite3_open(DATABASE, &db) != SQLITE_OK){
        printf("error:%s\n", sqlite3_errmsg(db));
        return -1;
    }

    const char *create_user_table = "CREATE TABLE IF NOT EXISTS user("
        "name TEXT PRIMARY KEY NOT NULL,"
        "pass TEXT NOT NULL);";

    const char *create_record_table = "CREATE TABLE IF NOT EXISTS record("
        "name TEXT NOT NULL,"
        "data TEXT NOT NULL,"
        "word TEXT NOT NULL);";

    char *errmsg;
    if(sqlite3_exec(db, create_user_table, NULL, NULL, &errmsg) != SQLITE_OK){
        printf("创建用户表失败:%s\n", errmsg);
        return -1;
    }

    if(sqlite3_exec(db, create_record_table, NULL, NULL, &errmsg) != SQLITE_OK){
        printf("创建记录表失败:%s\n", errmsg);
        return -1;
    }

    //创建监听套接字
    if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        perror("创建套接字失败：");
        return -1;
    }

    bzero(&server_addr, sizeof(server_addr)); //清空结构体
    server_addr.sin_family = AF_INET; //IPv4
    server_addr.sin_port = htons(atoi(argv[2])); //端口号
    server_addr.sin_addr.s_addr = inet_addr(argv[1]); //IP地址

    if(bind(listenfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){
        perror("绑定失败：");
        close(listenfd);
        return -1;
    }

    //监听
    if(listen(listenfd, 5) < 0){
        perror("监听失败：");
        close(listenfd);
        return -1;
    }

    signal(SIGCHLD, handler); //注册SIGCHLD信号处理函数，避免僵尸进程占用资源

    while(1){
        if((connectfd = accept(listenfd, NULL, NULL)) < 0){
            perror("accept失败：");
            break;
        }

        if((pid = fork()) < 0){
            perror("fork失败：");
            break;
        }else if(pid == 0){
            close(listenfd);
            //调用处理客户端请求函数
            handle_client(connectfd, db);
            exit(0);
        }else{
            //父进程负责连接
            close(connectfd);
        }
    }
    return 0;
}